/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.huawei.spare.part.replace.views.prepare.defective;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.huawei.spare.part.replace.framework.SmartContentPresenter;
import com.huawei.spare.part.replace.framework.SmartDataBus;
import com.huawei.spare.part.replace.framework.SmartDatabase;
import com.huawei.spare.part.replace.models.Project;
import com.huawei.spare.part.replace.models.SwitchMatchedPackage;
import com.huawei.spare.part.replace.utils.Application;
import com.huawei.spare.part.replace.utils.ZipUtils;
import com.huawei.spare.part.replace.views.widgets.Alerts;
import com.huawei.spare.part.replace.views.widgets.FxUtils;
import io.reactivex.Observable;
import io.reactivex.rxjavafx.observables.JavaFxObservable;
import io.reactivex.rxjavafx.schedulers.JavaFxScheduler;
import io.reactivex.schedulers.Schedulers;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.stage.FileChooser;
import net.lingala.zip4j.exception.ZipException;
import org.apache.commons.io.FileUtils;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.controlsfx.validation.decoration.CompoundValidationDecoration;
import org.controlsfx.validation.decoration.GraphicValidationDecoration;
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import org.controlsfx.validation.decoration.ValidationDecoration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.filechooser.FileSystemView;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.huawei.spare.part.replace.Constants.*;

/**
 * @author Woo
 */
public class DefectivePresenter extends SmartContentPresenter {

    private static final Logger log = LoggerFactory.getLogger(DefectivePresenter.class);

    @FXML ComboBox<String> boardTypeCombo;
    @FXML ComboBox<String> softwareVersionCombo;
    @FXML ComboBox<String> cpldVersionCombo;
    @FXML ComboBox<String> isStackCombo;
    @FXML CheckBox ignoreCX910BaseCbx;

    @Override
    public void initialize() {

        Platform.runLater(() -> {
            // addFormValidation();
//            loading("common.message.loading", false);
            final SmartDatabase DB = SmartDatabase.get();
            JavaFxObservable.changesOf(boardTypeCombo.valueProperty()).subscribe((change) -> {
                log.info("Board Type Combo changed, reload other combo's data");
                final String series = change.getNewVal();
                if (!Strings.isNullOrEmpty(series)) {
                    ignoreCX910BaseCbx.setVisible(BOARD_TYPE_BASE_IGNORE.contains(series.toUpperCase()));

                    final List<SwitchMatchedPackage> matches = DB.getSwitchMatchedPackage(series);
                    final List<String> softwareVersions = matches.stream()
                                                                 .map(SwitchMatchedPackage::getSoftwareVersion)
                                                                 .distinct()
                                                                 .sorted()
                                                                 .collect(Collectors.toList());
                    softwareVersionCombo.getItems().clear();
                    softwareVersionCombo.getItems().addAll(softwareVersions);
                    if (softwareVersionCombo.getSelectionModel().isEmpty()) {
                        softwareVersionCombo.getSelectionModel().select(0);
                    }

                    final List<String> cpldVersions = matches.stream()
                                                             .map(SwitchMatchedPackage::getCpldVersion)
                                                             .sorted()
                                                             .distinct()
                                                             .collect(Collectors.toList());
                    cpldVersionCombo.getItems().clear();
                    cpldVersionCombo.getItems().addAll(cpldVersions);
                    if (cpldVersionCombo.getSelectionModel().isEmpty()) {
                        cpldVersionCombo.getSelectionModel().select(0);
                    }
                }

                log.info("Board Type Combo changed, reload other combo data done");
            });

            Observable.defer((Callable<Observable<ObservableList<String>>>) () -> {
                return Observable.just(DB.getSwitchBordTypes());
            }).subscribeOn(Schedulers.io()).observeOn(JavaFxScheduler.platform()).subscribe((boardTypeList) -> {
                // prepare combo box data list
                boardTypeCombo.setItems(boardTypeList);
                isStackCombo.setItems(FXCollections.observableArrayList(Lists.newArrayList(getString("constants.dict.yes", "constants" +
                        ".dict.no"))));

                // setup auto complete
                FxUtils.autoCompleteComboBoxPlus(boardTypeCombo, (typedText, item) -> item.toLowerCase().contains(typedText.toLowerCase()));
                FxUtils.autoCompleteComboBoxPlus(softwareVersionCombo, (typedText, item) -> item.toLowerCase().contains(typedText
                        .toLowerCase()));

                // ;( fix wired arrow issue
                cpldVersionCombo.setEditable(false);
                isStackCombo.setEditable(false);
                boardTypeCombo.getSelectionModel().select(0);
                isStackCombo.getSelectionModel().select(0);

                dismissLoading();
            }, (error) -> {
                log.error("failed", error);
                dismissLoading();
            });
        });
    }

    private void addFormValidation() {
        ValidationSupport support = new ValidationSupport();
        support.registerValidator(boardTypeCombo, Validator.createEmptyValidator("不能为空"));
        ValidationDecoration iconDecorator = new GraphicValidationDecoration();
        ValidationDecoration cssDecorator = new StyleClassValidationDecoration();
        ValidationDecoration compoundDecorator = new CompoundValidationDecoration(cssDecorator, iconDecorator);
        support.setValidationDecorator(compoundDecorator);

        support.validationResultProperty().addListener((o, oldValue, newValue) -> {
            log.info("validation result: {}", newValue);
        });
    }

    @FXML
    private void importSwitchLog(ActionEvent event) {
        FileChooser chooser = new FileChooser();
        chooser.setTitle(getString("defective.log.file.chooser.title"));
        chooser.setInitialDirectory(FileSystemView.getFileSystemView().getHomeDirectory());
        chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("switch log file(*.zip)", "*.zip"));
        File file = chooser.showOpenDialog(SmartDataBus.get().getStage());
        if (file != null) {

            log.info("Find defective meta from log file : {}", file.getPath());
            final long uniqueId = new Date().getTime();
            final String destLogZipFileName = uniqueId + ".zip";
            final String logFilFolder = Application.getTempFolder() + File.separator + uniqueId;
            final File logFile = new File(logFilFolder + File.separator + DefectiveLogFileName);
            try {
                ZipUtils.extract(file, DefectiveLogFilePath, Application.getTempFolder(), destLogZipFileName);
                log.info("extract log zip file to {}", Application.getTempFolder() + File.separator + destLogZipFileName);
                ZipUtils.unzip(Application.getTempFile(destLogZipFileName), logFilFolder);
                log.info("extract log file to {}", logFile.getPath());
            } catch (ZipException e) {

            }

            try {
                final String content = FileUtils.readFileToString(logFile, UTF8);
                log.info("Read log file, content size {}", logFile.length());
                final String boardType = getMatchedContentGroup(content, BoardTypePatter, 1);
                log.info("Detect board type from log: {}", boardType);
                final String cpldVersion = getMatchedContentGroup(content, CpldVersionPatter, 1);
                log.info("Detect CPLD version from log: {}", cpldVersion);
                final String softwareVersion = getMatchedContentGroup(content, SoftwareVersionPatter, 1);
                log.info("Detect software version from log: {}", softwareVersion);
                final String stackPorts = getMatchedContentGroup(content, StackPortPatter, 1);
                log.info("Detect stack port from log: {}", stackPorts);

                boardTypeCombo.valueProperty().set(boardType.split("_")[0]);
                log.info("Reset board type combo value: {}", boardType.split("_")[0]);

                softwareVersionCombo.valueProperty().set("V" + softwareVersion);
                log.info("Reset software version value: {}", "V" + softwareVersion);

                cpldVersionCombo.valueProperty().set("V" + cpldVersion);
                log.info("Reset cpld version value: {}", "V" + cpldVersion);

                isStackCombo.getSelectionModel().select(stackPorts != null ? 0 : 1);
                log.info("Reset is stack combo value: {}", stackPorts != null ? 0 : 1);

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                FileUtils.deleteQuietly(Application.getTempFile(destLogZipFileName));
                FileUtils.deleteQuietly(new File(logFilFolder));
            }

        }
    }

    private String getMatchedContentGroup(String content, Pattern patter, int group) {
        final Matcher matcher = patter.matcher(content);
        if (matcher.find()) {
            log.info("find keyword by patter `{}`, content is `{}`", patter, matcher.group(0));
            return matcher.group(group).trim();
        }
        return null;
    }

    @Override
    public Observable<Boolean> onFinish() {
        final Project.Defective defective = new Project.Defective();
        defective.setBoardType(boardTypeCombo.valueProperty().get());
        defective.setSoftwareVersion(softwareVersionCombo.valueProperty().get());
        defective.setCpldVersion(cpldVersionCombo.valueProperty().get());
        defective.setIsStacked(isStackCombo.getSelectionModel().getSelectedIndex() == 0);
        defective.setIgnoreCX910Base(ignoreCX910BaseCbx.isSelected());

        final SwitchMatchedPackage matchedPackage = SmartDatabase.get()
                                                                 .findSwitchMatchedPackage(defective.getBoardType(), defective
                                                                         .getSoftwareVersion());
        defective.setIsV5(matchedPackage.getIsV5());
        defective.setMatchedPackage(matchedPackage);

        Project project = SmartDataBus.get().getProject();
        project.setDefective(defective);
        project.setSwitch0(SmartDatabase.get().getSwitchByBoardType(defective.getBoardType(), false));

        if (BOARD_TYPE_BASE_IGNORE.contains(defective.getBoardType().toUpperCase()) && !defective.getIgnoreCX910Base()) {
            final Optional<ButtonType> confirm = Alerts.confirm("defective.cx910.ge.10ge.case.title",
                    "defective.cx910.ge.10ge.case.confirm");
            if(!confirm.get().equals(ButtonType.OK)) {
                return Observable.just(false);
            }
        }

        if (!matchedPackage.getCpldVersion().equalsIgnoreCase(defective.getCpldVersion())) {
            final String message = String.format(getString("defective.cpld.not.match"), defective.getCpldVersion(), matchedPackage
                    .getCpldVersion());
            final Optional<ButtonType> confirm = Alerts.confirm("common.title.op.confirm", message);
            return Observable.just(confirm.get().equals(ButtonType.OK));
        }

        return Observable.just(true);
    }
}
